package com.r2coding.storage.engine.core;

import cn.hutool.core.lang.Assert;
import com.r2coding.cache.core.constants.CacheConstants;
import com.r2coding.core.exception.Pan4jFrameworkException;
import com.r2coding.storage.engine.core.context.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;

import java.io.IOException;
import java.util.Objects;

/**
 * @author 98k灬
 * @version v1.0.0
 * @Description : 顶级文件存储引擎的公用父类
 * @Create on : 2023/10/13 12:18
 **/
public abstract class AbstractStorageEngine implements StorageEngine {
    @Autowired
    private CacheManager cacheManager;

    /**
     * 公用的获取缓存的方法
     *
     * @return 缓存
     */
    protected Cache getCache() {
        if (Objects.isNull(cacheManager)) {
            throw new Pan4jFrameworkException("the cache manager is empty!");
        }
        return cacheManager.getCache(CacheConstants.CACHE_NAME);
    }

    /**
     * 存储物理文件
     * 1、参数校验
     * 2、执行存储动作
     *
     * @param storeFileContext 存储信息
     * @throws IOException IO异常
     */
    @Override
    public void store(StoreFileContext storeFileContext) throws IOException {
        this.checkStoreFileContext(storeFileContext);
        this.doStore(storeFileContext);
    }

    /**
     * 删除物理文件
     *
     * @param deleteFileContext 删除信息
     * @throws IOException IO异常
     */
    @Override
    public void delete(DeleteFileContext deleteFileContext) throws IOException {
        this.checkDeleteFileContext(deleteFileContext);
        this.doDelete(deleteFileContext);
    }

    /**
     * 存储物理文件的分片
     *
     * @param context 上下文
     */
    @Override
    public void storeChunk(StoreFileChunkContext context) throws IOException {
        this.checkStoreFileChunk(context);
        this.doStoreChunk(context);
    }

    /**
     * 文件合并
     *
     * @param context 上下文
     * @throws IOException IO异常
     */
    @Override
    public void mergeFile(MergeFileContext context) throws IOException {
        this.checkMergeFileContext(context);
        this.doMergeFile(context);
    }

    /**
     * 读取文件内容 写入到输出流中
     *
     * @param context 上下文
     * @throws IOException IO异常
     */
    @Override
    public void readFile(ReadFileContext context) throws IOException {
        this.checkReadFileContext(context);
        this.doReadFile(context);
    }

    protected abstract void doReadFile(ReadFileContext context) throws IOException;

    private void checkReadFileContext(ReadFileContext context) {
        Assert.notBlank(context.getRealPath(), "文件真实存储路径不能为空");
        Assert.notNull(context.getOutputStream(), "文件输出流不能为空");
    }

    protected abstract void doMergeFile(MergeFileContext context) throws IOException;

    private void checkMergeFileContext(MergeFileContext context) {
        Assert.notBlank(context.getFilename(), "文件名称不能为空");
        Assert.notBlank(context.getIdentifier(), "文件唯一标识不能为空");
        Assert.notNull(context.getUserId(), "当前用户ID不能为空");
        Assert.notEmpty(context.getRealPathList(), "文件分片列表不能为空");
    }

    protected abstract void doStoreChunk(StoreFileChunkContext context) throws IOException;

    private void checkStoreFileChunk(StoreFileChunkContext context) {
        Assert.notBlank(context.getFilename(), "文件名称不能为空");
        Assert.notBlank(context.getIdentifier(), "文件唯一标识不能为空");
        Assert.notNull(context.getTotalSize(), "文件总大小不能为空");
        Assert.notNull(context.getInputStream(), "文件分片不能为空");
        Assert.notNull(context.getTotalChunks(), "文件分片总数不能为空");
        Assert.notNull(context.getChunkNumber(), "文件分片下标不能为空");
        Assert.notNull(context.getCurrentChunkSize(), "文件分片下的大小不能为空");
        Assert.notNull(context.getUserId(), "当前用户ID不能为空");
    }

    protected abstract void doStore(StoreFileContext storeFileContext) throws IOException;

    protected abstract void doDelete(DeleteFileContext deleteFileContext) throws IOException;

    private void checkStoreFileContext(StoreFileContext context) {
        Assert.notBlank(context.getFilename(), "文件名称不能为空");
        Assert.notNull(context.getTotalSize(), "文件总大小不能为空");
        Assert.notNull(context.getInputStream(), "文件不能为空");
    }

    private void checkDeleteFileContext(DeleteFileContext context) {
        Assert.notEmpty(context.getRealFilePathList(), "要删除的文件路径列表不能为空");
    }
}
